#include "CapturePktInterface.h"


bool CapturePktInterface::Initialize(HandleNetworkPktCallback* callback)
{
	if (bInitialized)
		return bInitialized;

	if (callback == nullptr)
		return false;
	mPktHandle = callback;

	pcap_if_t *alldevs;
	pcap_if_t *d;
	char errbuf[PCAP_ERRBUF_SIZE];
	unsigned int netmask;
	int inum;
	pcap_t *adhandle;
	char packet_filter[] = "tcp and port 554";
	struct bpf_program fcode;
	int i = 0;

	/* Retrieve the device list */
	if (pcap_findalldevs(&alldevs, errbuf) == -1)
	{
		fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
		return false;
	}

	/* Print the list */
	for (d = alldevs; d; d = d->next)
	{
		printf("%d. %s", ++i, d->name);
		if (d->description)
			printf(" (%s)\n", d->description);
		else
			printf(" (No description available)\n");
	}

	if (i == 0)
	{
		printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
		return false;
	}

	printf("Enter the interface number (1-%d):", i);
	scanf("%d", &inum);

	if (inum < 1 || inum > i)
	{
		printf("\nInterface number out of range.\n");
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return false;
	}

	/* Jump to the selected adapter */
	for (d = alldevs, i = 0; i < inum - 1;d = d->next, i++);

	/* Open the adapter */
	if ((adhandle = pcap_open_live(d->name,	// name of the device
		2000,			// portion of the packet to capture. 
						// 65536 grants that the whole packet will be captured on all the MACs.
		0,				// promiscuous mode (nonzero means promiscuous)
		2000,			// read timeout
		errbuf			// error buffer
	)) == NULL)
	{
		fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return false;
	}

	/* Check the link layer. We support only Ethernet for simplicity. */
	if (pcap_datalink(adhandle) != DLT_EN10MB)
	{
		fprintf(stderr, "\nThis program works only on Ethernet networks.\n");
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return false;
	}

	if (d->addresses != NULL)
		/* Retrieve the mask of the first address of the interface */
		netmask = (unsigned int)((struct sockaddr_in *)(d->addresses->netmask)->sin_addr.S_un.S_addr);
	else
		/* If the interface is without addresses we suppose to be in a C class network */
		netmask = 0xffffff;

	//compile the filter
	if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) < 0)
	{
		fprintf(stderr, "\nUnable to compile the packet filter. Check the syntax.\n");
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return false;
	}

	//set the filter
	if (pcap_setfilter(adhandle, &fcode) < 0)
	{
		fprintf(stderr, "\nError setting the filter.\n");
		/* Free the device list */
		pcap_freealldevs(alldevs);
		return false;
	}
	printf("\nlistening on %s...\n", d->description);

	/* At this point, we don't need any more the device list. Free it */
	pcap_freealldevs(alldevs);

	mAdReader= adhandle;
	bInitialized = true;
	return bInitialized;
}

bool CapturePktInterface::Start()
{
	if (bInitialized == false)
		return bInitialized;
	Stop();
	bKeepAlive = true;
	mThread = new std::thread(ThreadFunc, this);
	if (mThread == nullptr)
		return false;

	bInitialized = false;
	return true;
}

void CapturePktInterface::Stop()
{
	bKeepAlive = false;
	if (mThread) {
		mThread->detach();
		mThread = nullptr;
	}
}

void CapturePktInterface::ThreadFunc(void * arg)
{
	CapturePktInterface *capturer = reinterpret_cast<CapturePktInterface*>(arg);
	if (capturer == nullptr)
		return;//should be here

	int res = -1;
	struct pcap_pkthdr *header;
	unsigned char *pkt;
	while (capturer->bKeepAlive && (res = pcap_next_ex(capturer->mAdReader, &header, &pkt)) >= 0)
	{
		CNetworkPacket *nPkt = new CNetworkPacket();
		capturer->mPktHandle(nPkt);
	}

	pcap_close(capturer->mAdReader);
}


CNetworkPacket::CNetworkPacket()
{
}

CNetworkPacket::~CNetworkPacket()
{
}
